// GCC header includes to get the parse tree declarations. The order
// is important and doesn't follow any kind of logic.
//

#include <stdlib.h>
#include <gmp.h>
#include <assert.h>
#include <cstdlib> // Include before GCC poison some declarations.

#include <signal.h>

extern "C"
{
#include "gcc-plugin.h"

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-iterator.h"
#include "intl.h"

#include "tm.h"

#include "diagnostic.h"
#include "c-common.h"
#include "c-pragma.h"
#include "function.h"

    // for some macros...
#include "cp/cp-tree.h"

#include "toplev.h"
}

#include <string>
#include <list>

#define SHOW_TREE_CODE_NAME(t)                                          \
    do {                                                                \
        if (t)                                                          \
            fprintf(stderr, " .. %s\n", tree_code_name[TREE_CODE(t)]);  \
        else                                                            \
            fprintf(stderr, " .. <nil>\n");                             \
    } while (0)

enum {
    T_VOID,

    T_UNKNOWN,

    T_INT,

    T_REAL,

    T_SU,

    T_FUNC,

    T_PTR,
};

typedef void *Type;

typedef std::list<Type>         Types;

struct Void {
    int                         type;

    Void() : type(T_VOID)
    {
    }
};

static void
_str_append_number(std::string& s, int n)
{
    char buf[16];
    sprintf(buf, "%d", n);
    s += buf;
}

struct Unknown {
    int                         type;

    Unknown() : type(T_UNKNOWN)
    {
    }
};

struct Int {
    int                         type;

    Int() : type(T_INT)
    {
    }
};

struct Real {
    int                         type;

    Real() : type(T_REAL)
    {
    }
};

struct Func {
    int                         type;

    std::string                 name;

    int                         lineno;

    std::string                 filename;

    Type                        return_type;

    Types                       arguments;

    Func() : type(T_FUNC)
           , lineno(0)
           , return_type(NULL)
    {
    }
};

struct Ptr {
    int                         type;

    int                         nptrs;

    Type                        real_type;

    Ptr() : type(T_PTR)
          , nptrs(0)
          , real_type(NULL)
    {
    }
};

// struct or union
struct SUDecl;

typedef std::list<SUDecl *>     SUDecls;

struct SUDecl {
    std::string                 name;

    int                         lineno;

    std::string                 filename;

    SUDecls                     children;

    SUDecl                     *parent;

    Types                       fields;

    bool                        is_union;

    SUDecl() : lineno(0)
             , parent(NULL)
             , is_union(false)
    {
    }

    ~SUDecl()
    {
        SUDecls::iterator i = children.begin();
        while (i != children.end())
            delete *i++;
    }
};

struct SU {
    int                         type;

    std::string                 name;

    SUDecl                     *su_lookup;

    SU() : type(T_SU)
         , su_lookup(NULL)
    {
    }
};

struct FuncCall {
    std::string                 name;

    int                         line;

    FuncCall() : line(0)
    {
    }

    FuncCall(const char *n, int l) : name(n), line(l)
    {
    }
};

typedef std::list<FuncCall *>   FuncCalls;

typedef std::list<Func *>       Funcs;

static SUDecl                  *g_curr_su = NULL;

static SUDecls                  g_sus;

// all function called
static FuncCalls                g_func_calls;

static Funcs                    g_funcs;

int plugin_is_GPL_compatible;

void
print_decl (tree decl);

static inline const char *
_get_decl_name(tree decl)
{
    tree id (DECL_NAME (decl));
    const char* name (id
                      ? IDENTIFIER_POINTER (id)
                      : "<unnamed>");
    return name;
}

static inline int
_get_real_lineno(int pos)
{
    const struct line_map *map
        = linemap_lookup (line_table, pos);

    if (map == NULL)
        return -1;

    return SOURCE_LINE (map, pos);
}

static inline void
_print_ident(int depth)
{
    while (depth-- > 0)
        fprintf(stderr, "  ");
}

static void
_format_tree_list(tree t, std::string& r)
{
    while (t) {
        tree v = TREE_VALUE(t);

        if (v) {
            if (!r.empty())
                r += " ";

            r += tree_code_name[TREE_CODE(v)];
        }

        t = TREE_CHAIN(t);
    }
}

static void
_type_to_string(Type type, std::string& res)
{
    res.clear();

    if (type == NULL)
        return;

    int code = * (int *) type;
    switch (code) {
    case T_VOID:
    {
        res = "void";
    }
        break;

    case T_UNKNOWN:
        res = "unknown";
        break;

    case T_INT:
        res = "int";
        break;

    case T_REAL:
        res = "real";
        break;

    case T_SU:
    {
        SU *su = (SU *) type;
        res = "struct_or_union ";
        res += su->name;
    }
        break;

    case T_FUNC:
    {
        Func *func = (Func *) type;
        std::string tmp;
        _type_to_string(func->return_type, tmp);
        res = tmp;

        res += " ";
        res += func->name;
        res += "(";

        Types::iterator cur = func->arguments.begin();
        while (cur != func->arguments.end()) {
            Type arg = *cur++;

            _type_to_string(arg, tmp);
            res += tmp;

            if (cur != func->arguments.end())
                res += ", ";
        }

        res += ")";
    }
        break;

    case T_PTR:
    {
        Ptr *ptr = (Ptr *) type;

        _type_to_string(ptr->real_type, res);

        res += " ";

        int i = ptr->nptrs;
        while (i-- > 0) {
            res += "*";
        }
    }
        break;

    default:
        assert(0);
        break;
    }
}

void
print_decl (tree decl)
{
}

void
traverse (tree ns)
{
}

/* stolen from gcc/cp/error.h */
location_t
location_of (tree t)
{
    if (!t)
        return UNKNOWN_LOCATION;

    if (TREE_CODE (t) == PARM_DECL && DECL_CONTEXT (t))
        t = DECL_CONTEXT (t);
    else if (TYPE_P (t))
        t = TYPE_MAIN_DECL (t);
    else if (TREE_CODE (t) == OVERLOAD)
        t = OVL_FUNCTION (t);

    if (!t)
        return UNKNOWN_LOCATION;

    if (DECL_P(t))
        return DECL_SOURCE_LOCATION (t);
    else if (EXPR_P(t) && EXPR_HAS_LOCATION(t))
        return EXPR_LOCATION(t);

    return UNKNOWN_LOCATION;
}

static void
_print_func_decl(tree decl)
{
    tree type = TREE_TYPE(decl);

    if (type && TREE_CODE(decl) == FUNCTION_DECL) {
        tree arguments = TYPE_ARG_TYPES(type);
        fprintf(stderr, "arguments:\n");
        while (arguments) {
            tree v = TREE_VALUE(arguments);

            fprintf(stderr, " -> %s\n", tree_code_name[TREE_CODE(v)]);

            arguments = TREE_CHAIN(arguments);
        }

        // 打印返回类型
        // DECL_RESULT 在函数声明，但没有定义的时候，会返回 NULL_TREE
        // 要用以下方法得到返回的类弄
        // 一些老代码是没有返回类型的，这种老函数默认是返回 int
        if (TREE_TYPE(decl))
            fprintf(stderr, "function return type: %s\n", tree_code_name[TREE_CODE(TREE_TYPE(TREE_TYPE(decl)))]);
        else
            fprintf(stderr, "default <int> function return type\n");
    }
}

static const char *_get_real_su_name(tree type)
{
    type = TYPE_MAIN_VARIANT(type);

    if (type) {
        tree tn = TYPE_NAME(type);
        if (tn && TREE_CODE(tn) == IDENTIFIER_NODE)
            return IDENTIFIER_POINTER(tn);
    }

    tree tn = TYPE_NAME(type);
    if (tn && TREE_CODE(tn) == IDENTIFIER_NODE)
        return IDENTIFIER_POINTER(tn);

    return NULL;
}

static Type
_create_type_from_tree(tree t)
{
    if (!t)
        return NULL;

    int code = TREE_CODE(t);

    switch (code) {
    case BOOLEAN_TYPE:
    case ENUMERAL_TYPE:
    case INTEGER_TYPE:
        return new Int();
        break;

    case REAL_TYPE:
        new Real();
        break;

    case RECORD_TYPE:
    case UNION_TYPE:
    {
        if (code == UNION_TYPE)
            fprintf(stderr, "yes, it's union\n");

        SU *su = new SU();

        const char *name = _get_real_su_name(t);
        if (name)
            su->name = name;

        return su;
    }
        break;

    case POINTER_TYPE:
    case REFERENCE_TYPE:
    {
        int cnt = 0;
        while (POINTER_TYPE_P(t)) {
            if (TREE_CODE(t) == POINTER_TYPE)
                ++cnt;
            t = TREE_TYPE(t);
        }
        Type real_type = _create_type_from_tree(t);
        if (real_type) {
            if (cnt > 0) {
                Ptr *p = new Ptr();
                p->real_type = real_type;
                p->nptrs = cnt;
                return p;
            }
            else
                return real_type;
        }
    }
        break;

    case VOID_TYPE:
        return new Void();
        break;

    default:
        break;
    }

    return NULL;
}

static void
_collect_func_decl(tree decl)
{
    tree type = TREE_TYPE(decl);

    if (type && TREE_CODE(decl) == FUNCTION_DECL) {
        Type return_type = NULL;

        if (TREE_TYPE(type)) {
            return_type = _create_type_from_tree(TREE_TYPE(type));
        }

        Func *func = new Func();
        func->return_type = return_type;

        func->lineno   = DECL_SOURCE_LINE(decl);
        func->filename = DECL_SOURCE_FILE(decl);

        func->name = _get_decl_name(decl);

        // {
        //     tree args = DECL_ARGUMENTS(decl);
        //     while (args) {
        //         fprintf(stderr, " . %s %s\n",
        //                 tree_code_name[TREE_CODE(args)],
        //                 _get_decl_name(args));
        //         tree xx = DECL_ARG_TYPE(args);
        //         if (xx) {
        //             xx = TYPE_NAME(xx);
        //             if (TREE_CODE(xx) == TYPE_DECL) {
        //                 tree jj = DECL_NAME(xx);
        //                 if (jj)
        //                     fprintf(stderr, " .... %s\n", IDENTIFIER_POINTER(jj));
        //                 xx = DECL_ORIGINAL_TYPE(xx);
        //             }

        //             if (xx)
        //                 fprintf(stderr, " .. %s %s\n",
        //                         tree_code_name[TREE_CODE(xx)],
        //                         IDENTIFIER_POINTER(TYPE_NAME(xx)));
        //         }
        //         args = TREE_CHAIN(args);
        //     }
        // }

        tree arguments = TYPE_ARG_TYPES(type);
        while (arguments) {
            tree v = TREE_VALUE(arguments);
            Type arg = _create_type_from_tree(v);
            if (arg != NULL)
                func->arguments.push_back(arg);

            arguments = TREE_CHAIN(arguments);
        }

        g_funcs.push_back(func);
    }
}

static void
_print_enum_decl(tree e)
{
    if (e) {
        tree name = TYPE_NAME(e);

        fprintf(stderr, "enum %s\n",
                name
                ? IDENTIFIER_POINTER (name)
                : "<unnamed>");

        tree fields = TYPE_FIELDS(e);
        while (fields) {
            tree name = TREE_PURPOSE(fields);
            fprintf(stderr, " -> %s\n",
                    name
                    ? IDENTIFIER_POINTER(name)
                    : "<unnamed>");
            fields = TREE_CHAIN(fields);
        }
    }
}

static void
_print_record_decl(tree t)
{
    if (t) {
        // maybe it's the typedef
        
    }
}

static void
_collect_func_call(tree body, int depth)
{
    if (!body)
        return;

    int code = TREE_CODE(body);

    const char *fn = NULL;
    int call_line = 0;

    if (code == CALL_EXPR) {
        call_line = EXPR_LINENO(body);
        tree tmp = CALL_EXPR_FN(body);
        if (tmp && TREE_CODE(tmp) == ADDR_EXPR) {
            tmp = TREE_OPERAND(tmp, 0);
            if (tmp && TREE_CODE (tmp) == FUNCTION_DECL) {
                fn = _get_decl_name(tmp);

                if (fn)
                    g_func_calls.push_back(new FuncCall(fn, call_line));
            }
        }
    }

    // _print_ident(depth);

    // if (fn) {
    //     fprintf(stderr, "%s <call %s at %d>\n",
    //             tree_code_name[code], fn, call_line);
    // }
    // else {
    //     fprintf(stderr, "%s\n",
    //             tree_code_name[code]);
    // }

    if (code == STATEMENT_LIST) {
        tree_stmt_iterator it = tsi_start(body);
        while (it.ptr) {
            tree stmt = it.ptr->stmt;

            if (TREE_CODE(stmt) == BIND_EXPR)
                _collect_func_call(BIND_EXPR_BODY(stmt), depth + 1);
            else
                _collect_func_call(stmt, depth + 1);

            tsi_next(&it);
        }
    }
    else {
        if (EXPR_P(body)) {
            int oplen = TREE_OPERAND_LENGTH(body);
            int i;
            for (i = 0; i < oplen; i++) {
                tree op = TREE_OPERAND(body, i);
                _collect_func_call(op, depth + 1);
            }
        }
    }
}

extern "C" void
cb_pre_genericize (void *gcc_data, void *user_data)
{
    // If there were errors during compilation,
    // let GCC handle the exit.
    //
    if (errorcount || sorrycount)
        return;

    // traverse (global_namespace);
    struct function *f = cfun;

    tree decl = f->decl;

    // fprintf(stderr, "  << %s: type %s, file %s, line %d >>\n",
    //         _get_decl_name(decl),
    //         tree_code_name[TREE_CODE(decl)],
    //         DECL_SOURCE_FILE(decl),
    //         DECL_SOURCE_LINE(decl));

    // 这是函数定义
    if (DECL_INITIAL(decl)) {
        // fprintf(stderr, "     defined between %d - %d\n",
        //         (f->function_start_locus == UNKNOWN_LOCATION)
        //         ? DECL_SOURCE_LINE(decl)
        //         : _get_real_lineno(f->function_start_locus),
        //         _get_real_lineno(f->function_end_locus));

        // which function called in this function
        tree body = DECL_SAVED_TREE(decl);
        _collect_func_call(body, 0);
    }

    _collect_func_decl(decl);
}

extern "C" void
cb_finish_parse(void *, void *)
{
    // dump the includes
    fprintf(stderr, "dump includes:\n");
    if (my_include_info) {
        my_include_info_t *curr = my_include_info;
        while (curr) {
            fprintf(stderr, " -> abs %s, header %s, line %d\n",
                    curr->dir ? curr->dir : "<NULL>",
                    curr->header, curr->lineno);
            curr = curr->next;
        }
    }

    // dump the macros
    // fprintf(stderr, "dump macros:\n");
    // if (my_macro_info) {
    //     my_macro_info_t *curr = my_macro_info;
    //     while (curr) {
    //         fprintf(stderr, "%s at %s: %d\n",
    //                 curr->def, curr->file_name, curr->line);
    //         curr = curr->next;
    //     }
    // }

    fprintf(stderr, "function called (%d):\n", g_func_calls.size());
    if (g_func_calls.size() > 0) {
        FuncCalls::iterator cur = g_func_calls.begin();
        while (cur != g_func_calls.end()) {
            FuncCall *f = *cur++;

            fprintf(stderr, " -> %s\n", f->name.c_str(), f->line);
        }
    }

    fprintf(stderr, "function decl:\n");
    if (g_funcs.size() > 0) {
        Funcs::iterator cur = g_funcs.begin();
        while (cur != g_funcs.end()) {
            Func *func = *cur++;

            std::string str;
            _type_to_string((Type) func, str);

            fprintf(stderr, "%s < %s %d >\n", str.c_str(),
                    func->filename.c_str(), func->lineno);
        }
    }
}

extern "C" void
cb_finish_decl(void *gcc_data, void *user_data)
{
    (void) user_data;

    if (gcc_data == NULL)
        return;

    tree decl = (tree) gcc_data;

    int decl_code = TREE_CODE(decl);

    switch (decl_code) {
    case TYPE_DECL:
    case VAR_DECL:
    case FUNCTION_DECL:
    case FIELD_DECL:
        // fprintf(stderr, " << %s %s %d (%s) >>\n",
        //         _get_decl_name(decl),
        //         DECL_SOURCE_FILE(decl),
        //         DECL_SOURCE_LINE(decl),
        //         tree_code_name[decl_code]);

        // if (decl_code == FUNCTION_DECL)
        //     _print_func_decl(decl);

        if (!DECL_CONTEXT(decl) || TRANSLATION_UNIT_DECL == TREE_CODE(DECL_CONTEXT(decl))) {
            // just care that in global scope.
            _collect_func_decl(decl);
        }

        break;

    default:
        // fprintf(stderr, "ignore %s\n", tree_code_name[decl_code]);
        break;
    }
}

extern "C" void
cb_finish_type(void *gcc_data, void *user_data)
{
    (void) user_data;

    if (gcc_data == NULL)
        return;

    tree type = (tree) gcc_data;

    int type_code = TREE_CODE(type);

    if (type_code == ENUMERAL_TYPE)
        _print_enum_decl(type);
    else if (type_code == RECORD_TYPE || type_code == UNION_TYPE) {
        _print_record_decl(type);

        assert(g_curr_su);
        tree name = TYPE_NAME(type);
        if (name)
            g_curr_su->name = IDENTIFIER_POINTER(name);

        if (type_code == UNION_TYPE)
            g_curr_su->is_union = true;
        else
            g_curr_su->is_union = false;

        // up
        g_curr_su = g_curr_su->parent;
    }
}

extern "C" void
cb_start_unit(void *gcc_data, void *user_data)
{
    (void) user_data;
    (void) gcc_data;

    my_preprecess_hook();
}

extern "C" void
cb_start_record(void *gcc_data, void *user_data)
{
    (void) user_data;
    (void) gcc_data;

    SUDecl *r = new SUDecl;

    if (g_curr_su) {
        // beginning a inner record type
        g_curr_su->children.push_back(r);
        r->parent = g_curr_su;
    }
    else {
        g_curr_su = r;
        r->parent = NULL;

        g_sus.push_back(r);
    }

    g_curr_su = r;
}

void fatal_signal(int signo)
{
    fprintf(stderr, "fatal signal got!\n");
    for (;;)
        ;
}

extern "C" int
plugin_init (plugin_name_args* info,
             plugin_gcc_version*)
{
    fprintf(stderr, "processing %s\n", main_input_filename);

    signal(SIGSEGV, fatal_signal);

    //
    // Parse options if any.
    //

    // Disable assembly output.
    //
    asm_file_name = HOST_BIT_BUCKET;

    // Register callbacks.
    //
    register_callback (info->base_name,
                       PLUGIN_START_UNIT,
                       &cb_start_unit,
                       0);

    // 在每个方法 genericize 前调用
    // 这可以获得 c 和 c++ 的语言独立的 AST
    register_callback (info->base_name,
                       PLUGIN_PRE_GENERICIZE,
                       &cb_pre_genericize,
                       0);

    register_callback (info->base_name,
                       PLUGIN_FINISH_PARSE,
                       &cb_finish_parse,
                       0);

    register_callback (info->base_name,
                       PLUGIN_FINISH_DECL,
                       &cb_finish_decl,
                       0);

    register_callback (info->base_name,
                       PLUGIN_FINISH_TYPE,
                       &cb_finish_type,
                       0);

    register_callback (info->base_name,
                       PLUGIN_START_RECORD,
                       &cb_start_record,
                       0);

    return 0;
}
